﻿using System;
using NLog;

namespace CoreLibrary.Service {

    public abstract class ServiceBase : IService {

        protected ServiceBase() {
            Name = GetType().Name + "#" + new object().GetHashCode();
            Id   = Guid.NewGuid().ToString();
            ServiceState = new ServiceState();
        }

        public string Name { get; set; }
        public string Id   { get; private set; }
        public ServiceState ServiceState { get; private set; }
        
        protected abstract void OnInit();
        protected abstract void OnDestroy();
        protected abstract void OnStart();
        protected abstract void OnStop();

        public void Init() {
            lock (this) {
                if (ServiceState.Code != ServiceState.Uninitialized) {
                    return;
                }
                try {
                    OnInit();
                    ServiceState.SetInitialized("Service initialized.");
                } 
                catch (Exception error) {
                    ServiceState.Message = "Service initialization failed: " + error.Message;
                    LogManager.GetCurrentClassLogger().ErrorException(
                        "Could not initialise " + Name + ". Error: " + error.Message, error
                    );
                }
            }
        }

        public void Start() {
            lock (this) {
                if (ServiceState.Code == ServiceState.Uninitialized ||
                    ServiceState.Code == ServiceState.Started ||
                    ServiceState.Code == ServiceState.Destroyed) {
                    return;
                }
                try {
                    OnStart();
                    ServiceState.SetStarted(Name + " Service started.");
                } catch (Exception error) {
                    ServiceState.Message = "Starting service failed: " + error.Message;
                    LogManager.GetCurrentClassLogger().ErrorException(
                        "Could not start " + Name + ". Error: " + error.Message, error
                    );
                }
            }
        }

        public void Stop() {
            lock (this) {
                if (ServiceState.Code != ServiceState.Started) {
                    return;
                }
                try {
                    OnStop();
                    ServiceState.SetStopped("Service stopped.");
                } catch (Exception error) {
                    ServiceState.Message = "Stoppping service failed: " + error.Message;
                    LogManager.GetCurrentClassLogger().ErrorException(
                        "Could not stop " + Name + ". Error: " + error.Message, error
                    );
                }
            }
        }

        public void Destroy() {
            lock (this) {
                try {
                    if (ServiceState.Code == ServiceState.Destroyed) {
                        return;
                    }
                    if (ServiceState.Code == ServiceState.Started) {
                        Stop();
                    }
                    if (ServiceState.Code == ServiceState.Started) {
                        throw new Exception("Can not destroy the service because it can not be stopped");
                    }
                    OnDestroy();
                    ServiceState.SetDestroyed("Service destroyed.");
                } 
                catch (Exception error) {
                    ServiceState.Message = "Destroying service failed: " + error.Message;
                    LogManager.GetCurrentClassLogger().ErrorException(
                        "Could not destroy " + Name + ". Error: " + error.Message, error
                    );
                }
            }
        }
       
    }
}